package Control;

import Boundary.Graphic;
import Boundary.Text;
import Entity.DieCup;
import Entity.GameBoard;
import Entity.Player;

public class BaseGame {
	private final int POINTS_TO_START_WITH = 1000;
	private final int POINTS_TO_WIN = 3000;
	private final int NUMBER_OF_PLAYERS = 2;
	
	protected DieCup dieCup;
	protected GameBoard gameBoard;
	protected Player[] players;
	
	protected Player currentPlayer;
	protected String shownText = "";
	// Player 1 always starts. However this would work with Player 2, or
	// even random.
	protected int activePlayer = 1;
	/**
	 * Game constructor. Creates new instances of the required classes.
	 */
	public BaseGame() {
		this.dieCup = new DieCup();
		this.gameBoard = new GameBoard();
		this.players = new Player[this.NUMBER_OF_PLAYERS];

		// Make all player-objekts in loop
		for (int i = 0; i < this.NUMBER_OF_PLAYERS; i++) {
			this.players[i] = new Player(this.POINTS_TO_START_WITH);
		}
	}

	/**
	 * Start the game.
	 */
	public void startGame() {
		Graphic.setupFields();

		// Set the player names and save them in the player objects
		this.players[0].setName("Spiller 1");
		this.players[1].setName("Spiller 2");

		this.shownText = Text.getRules();

		// adds the players to the GUI.
		for (int i = 0; i < this.NUMBER_OF_PLAYERS; i++) {
			this.players[i].setName("Player " + (i + 1));
			this.players[i].setName(i + Graphic.getUserString(Text.getNameRequest(i + 1)));
			Graphic.addPlayer(this.players[i].getName(), this.players[i].getBalance());
		}
		
		Graphic.showMessage(this.shownText);

		// Start of the actual game. Infinite loop is broken only when someone
		// wins or loses.
		while (true) {
			takeTurn();
		}
	}

	/**
	 * Prints the name of the current player, along with a message telling that
	 * he has won the game., then waits for input, to make sure the message
	 * stays on the screen. Ends the program when any input is given.
	 */
	void winTasks() {
		this.shownText += Text.getWinner(this.currentPlayer.getName(),
			this.currentPlayer.getBalance());
	finishGame();
}
	void loseTasks() {
		this.shownText += Text.getLoser(
			this.currentPlayer.getName(),
			this.currentPlayer.getBalance());
	finishGame();
}
	void finishGame() {
		Graphic.showMessage(this.shownText);
		cleanUp();
	}

	/**
	 * Simple method to get a player object from an integer. Eg. "getPlayer(1)"
	 * will get player1 object.
	 * 
	 * @param playerNo
	 *            The number of the player to get.
	 * @return The player object that corresponds to the number given.
	 */
	protected Player getPlayer(int playerNo) {
		if (playerNo <= this.players.length)  {
			return this.players[playerNo - 1];
		}
		return null;
	}

	/**
	 * Simple method to get the number of the next player.
	 * 
	 * @param input
	 *            The number to toggle away from.
	 * @return 2 if 1 is given etc., but gives 1 if the value for number of players is reached.
	 */
	protected int getNextPlayer(int input) {
		if (input + 1 > this.NUMBER_OF_PLAYERS) {
			return 1;
		}

		return input + 1;
	}

	protected boolean isGameWon() {
		return this.players[this.activePlayer - 1].getBalance() >= this.POINTS_TO_WIN;
	}

	protected void takeTurn() {
		int scoreToAdd;
		boolean isGameLost = false;
		// Get data for the active player
		this.currentPlayer = getPlayer(this.activePlayer);

		// Shake
		this.dieCup.shakeDieCup();

		Graphic.moveCar(this.currentPlayer.getName(), this.dieCup.getSum());
		
		// Add points from field
		scoreToAdd = this.gameBoard.getField(this.dieCup.getSum()).getFieldScore();
		if(!this.currentPlayer.addToAccount(scoreToAdd)) {
			isGameLost = true;
		}
		Graphic.setBalance(this.currentPlayer.getName(), this.currentPlayer.getBalance());

		// Write status/score to TUI/GUI
		statusTasks();

		// Check if player have won
		if (isGameWon()) {
			winTasks();
		}
		
		// Check if player have lost
		if (isGameLost) {
			loseTasks();
		}

		// Switch turn to the next player, unless the current player gets an extra turn.
		if (!this.gameBoard.getField(this.dieCup.getSum()).getGivesExtraTurn()) {
			this.activePlayer = getNextPlayer(this.activePlayer);
		}

		if (!isGameWon() && !isGameLost) {
			// Write whos turn it is and wait for input
			writeNextPlayersTurn();
		}
	}

	/**
	 * Writes score and dice values to both GUI and TUI
	 */
	void statusTasks() {
		Graphic.setDice(this.dieCup.getValueDie1(), this.dieCup.getValueDie2());
		this.shownText = Text.getStatus(this.players, this.dieCup.getSum());
	}

	void writeNextPlayersTurn() {
		this.shownText += Text.getTurn(getPlayer(this.activePlayer).getName());
		Graphic.showMessage(this.shownText);
	}

	/**
	 * Closes the program and cleans up properly.
	 */
	public void cleanUp() {
		Graphic.close();
		System.exit(0);
	}

}
